Prereq: "7.4p1"
*** ../tcl7.4p1/patchlevel.h	Fri Jul 28 10:08:25 1995
--- patchlevel.h	Mon Sep 18 11:41:25 1995
***************
*** 17,23 ****
   * See the file "license.terms" for information on usage and redistribution
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   *
!  * @(#) patchlevel.h 1.10 95/07/28 10:08:24
   */
  
! #define TCL_PATCH_LEVEL "7.4p1"
--- 17,23 ----
   * See the file "license.terms" for information on usage and redistribution
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   *
!  * @(#) patchlevel.h 1.11 95/09/18 11:41:22
   */
  
! #define TCL_PATCH_LEVEL "7.4p2"
*** ../tcl7.4p1/./tclBasic.c	Tue Jul 25 13:01:45 1995
--- ./tclBasic.c	Tue Sep  5 11:37:35 1995
***************
*** 12,18 ****
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   */
  
! static char sccsid[] = "@(#) tclBasic.c 1.169 95/07/25 13:01:44";
  
  #include "tclInt.h"
  #ifndef TCL_GENERIC_ONLY
--- 12,18 ----
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   */
  
! static char sccsid[] = "@(#) tclBasic.c 1.171 95/09/05 11:37:34";
  
  #include "tclInt.h"
  #ifndef TCL_GENERIC_ONLY
***************
*** 210,215 ****
--- 210,216 ----
  	    cmdPtr->clientData = (ClientData) NULL;
  	    cmdPtr->deleteProc = NULL;
  	    cmdPtr->deleteData = (ClientData) NULL;
+ 	    cmdPtr->deleted = 0;
  	    Tcl_SetHashValue(hPtr, cmdPtr);
  	}
      }
***************
*** 435,444 ****
      for (hPtr = Tcl_FirstHashEntry(&iPtr->commandTable, &search);
  	    hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  	cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
! 	if (cmdPtr->deleteProc != NULL) { 
! 	    (*cmdPtr->deleteProc)(cmdPtr->deleteData);
  	}
- 	ckfree((char *) cmdPtr);
      }
      Tcl_DeleteHashTable(&iPtr->commandTable);
      for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &search);
--- 436,448 ----
      for (hPtr = Tcl_FirstHashEntry(&iPtr->commandTable, &search);
  	    hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  	cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
! 	if (!cmdPtr->deleted) {
! 	    cmdPtr->deleted = 1;
! 	    if (cmdPtr->deleteProc != NULL) {
! 		(*cmdPtr->deleteProc)(cmdPtr->deleteData);
! 	    }
! 	    ckfree((char *) cmdPtr);
  	}
      }
      Tcl_DeleteHashTable(&iPtr->commandTable);
      for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &search);
***************
*** 533,538 ****
--- 537,550 ----
      Tcl_HashEntry *hPtr;
      int new;
  
+     if (iPtr->flags & DELETED) {
+ 	/*
+ 	 * The interpreter is being deleted.  Don't create any new
+ 	 * commands;  it's not safe to muck with the interpreter anymore.
+ 	 */
+ 
+ 	return (Tcl_Command) NULL;
+     }
      hPtr = Tcl_CreateHashEntry(&iPtr->commandTable, cmdName, &new);
      if (!new) {
  	/*
***************
*** 539,557 ****
  	 * Command already exists:  delete the old one.
  	 */
  
! 	cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
! 	if (cmdPtr->deleteProc != NULL) {
! 	    (*cmdPtr->deleteProc)(cmdPtr->deleteData);
! 	}
!     } else {
! 	cmdPtr = (Command *) ckalloc(sizeof(Command));
! 	Tcl_SetHashValue(hPtr, cmdPtr);
      }
      cmdPtr->hPtr = hPtr;
      cmdPtr->proc = proc;
      cmdPtr->clientData = clientData;
      cmdPtr->deleteProc = deleteProc;
      cmdPtr->deleteData = clientData;
      return (Tcl_Command) cmdPtr;
  }
  
--- 551,576 ----
  	 * Command already exists:  delete the old one.
  	 */
  
! 	Tcl_DeleteCommand(interp, Tcl_GetHashKey(&iPtr->commandTable, hPtr));
! 	hPtr = Tcl_CreateHashEntry(&iPtr->commandTable, cmdName, &new);
! 	if (!new) {
! 	    /*
! 	     * Drat.  The stupid deletion callback recreated the command.
! 	     * Just throw away the new command (if we try to delete it again,
! 	     * we could get stuck in an infinite loop).
! 	     */
! 
! 	     ckfree((char  *) Tcl_GetHashValue(hPtr));
! 	 }
      }
+     cmdPtr = (Command *) ckalloc(sizeof(Command));
+     Tcl_SetHashValue(hPtr, cmdPtr);
      cmdPtr->hPtr = hPtr;
      cmdPtr->proc = proc;
      cmdPtr->clientData = clientData;
      cmdPtr->deleteProc = deleteProc;
      cmdPtr->deleteData = clientData;
+     cmdPtr->deleted = 0;
      return (Tcl_Command) cmdPtr;
  }
  
***************
*** 699,704 ****
--- 718,732 ----
      Tcl_HashEntry *hPtr;
      Command *cmdPtr;
  
+     if (iPtr->flags & DELETED) {
+ 	/*
+ 	 * The interpreter is being deleted, so this command has already
+ 	 * been deleted, or will be soon.  It's not safe to muck with the
+ 	 * interpreter anymore.
+ 	 */
+ 
+ 	return -1;
+     }
      hPtr = Tcl_FindHashEntry(&iPtr->commandTable, cmdName);
      if (hPtr == NULL) {
  	return -1;
***************
*** 706,719 ****
      cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
  
      /*
!      * Delete the hash table entry before invoking the deletion callback;
!      * otherwise the callback could delete the command a second time, or
!      * create a new command on top of the one we're deleting.
       */
  
!     Tcl_DeleteHashEntry(hPtr);
      if (cmdPtr->deleteProc != NULL) {
  	(*cmdPtr->deleteProc)(cmdPtr->deleteData);
      }
      ckfree((char *) cmdPtr);
      return 0;
--- 734,772 ----
      cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
  
      /*
!      * The code here is tricky.  We can't delete the hash table entry
!      * before invoking the deletion callback because there are cases
!      * where the deletion callback needs to invoke the command (e.g.
!      * object systems such as OTcl).  However, this means that the
!      * callback could try to delete or rename the command.  The deleted
!      * flag allows us to detect these cases and skip nested deletes.
       */
  
!     if (cmdPtr->deleted) {
! 	/*
! 	 * Another deletion is already in progress.  Remove the hash
! 	 * table entry now, but don't invoke a callback or free the
! 	 * command structure.
! 	 */
! 
! 	Tcl_DeleteHashEntry(hPtr);
! 	cmdPtr->hPtr = NULL;
! 	return 0;
!     }
!     cmdPtr->deleted = 1;
      if (cmdPtr->deleteProc != NULL) {
  	(*cmdPtr->deleteProc)(cmdPtr->deleteData);
+     }
+ 
+     /*
+      * Don't use hPtr to delete the hash entry here, because it's
+      * possible that the deletion callback renamed the command.
+      * Instead, use cmdPtr->hptr, and make sure that no-one else
+      * has already deleted the hash entry.
+      */
+ 
+     if (cmdPtr->hPtr != NULL) {
+ 	Tcl_DeleteHashEntry(cmdPtr->hPtr);
      }
      ckfree((char *) cmdPtr);
      return 0;
*** ../tcl7.4p1/./tclCmdMZ.c	Sun Apr 30 14:35:16 1995
--- ./tclCmdMZ.c	Fri Aug  4 09:57:59 1995
***************
*** 14,20 ****
   */
  
  #ifndef lint
! static char sccsid[] = "@(#) tclCmdMZ.c 1.57 95/04/30 14:35:17";
  #endif
  
  #include "tclInt.h"
--- 14,20 ----
   */
  
  #ifndef lint
! static char sccsid[] = "@(#) tclCmdMZ.c 1.58 95/08/04 09:58:00";
  #endif
  
  #include "tclInt.h"
***************
*** 122,128 ****
  	pattern = Tcl_DStringValue(&patternDString);
  	for (p = pattern; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(*p);
  	    }
  	}
  	Tcl_DStringInit(&stringDString);
--- 122,128 ----
  	pattern = Tcl_DStringValue(&patternDString);
  	for (p = pattern; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(UCHAR(*p));
  	    }
  	}
  	Tcl_DStringInit(&stringDString);
***************
*** 130,136 ****
  	string = Tcl_DStringValue(&stringDString);
  	for (p = string; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(*p);
  	    }
  	}
      } else {
--- 130,136 ----
  	string = Tcl_DStringValue(&stringDString);
  	for (p = string; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(UCHAR(*p));
  	    }
  	}
      } else {
***************
*** 270,276 ****
  	pattern = Tcl_DStringValue(&patternDString);
  	for (p = pattern; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(*p);
  	    }
  	}
  	Tcl_DStringInit(&stringDString);
--- 270,276 ----
  	pattern = Tcl_DStringValue(&patternDString);
  	for (p = pattern; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(UCHAR(*p));
  	    }
  	}
  	Tcl_DStringInit(&stringDString);
***************
*** 278,284 ****
  	string = Tcl_DStringValue(&stringDString);
  	for (p = string; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(*p);
  	    }
  	}
      } else {
--- 278,284 ----
  	string = Tcl_DStringValue(&stringDString);
  	for (p = string; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(UCHAR(*p));
  	    }
  	}
      } else {
***************
*** 1134,1140 ****
  	Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
  	for (p = interp->result; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(*p);
  	    }
  	}
  	return TCL_OK;
--- 1134,1140 ----
  	Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
  	for (p = interp->result; *p != 0; p++) {
  	    if (isupper(UCHAR(*p))) {
! 		*p = tolower(UCHAR(*p));
  	    }
  	}
  	return TCL_OK;
***************
*** 1150,1156 ****
  	Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
  	for (p = interp->result; *p != 0; p++) {
  	    if (islower(UCHAR(*p))) {
! 		*p = toupper(*p);
  	    }
  	}
  	return TCL_OK;
--- 1150,1156 ----
  	Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
  	for (p = interp->result; *p != 0; p++) {
  	    if (islower(UCHAR(*p))) {
! 		*p = toupper(UCHAR(*p));
  	    }
  	}
  	return TCL_OK;
*** ../tcl7.4p1/./tclVar.c	Fri Jun  2 09:29:15 1995
--- ./tclVar.c	Fri Aug  4 09:38:17 1995
***************
*** 14,20 ****
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   */
  
! static char sccsid[] = "@(#) tclVar.c 1.61 95/06/02 09:29:16";
  
  #include "tclInt.h"
  #include "tclPort.h"
--- 14,20 ----
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   */
  
! static char sccsid[] = "@(#) tclVar.c 1.62 95/08/04 09:38:18";
  
  #include "tclInt.h"
  #include "tclPort.h"
***************
*** 2016,2022 ****
  					 * indicates what's happening to
  					 * variable, plus other stuff like
  					 * TCL_GLOBAL_ONLY and
! 					 * TCL_INTERP_DESTROYED. */
  {
      register VarTrace *tracePtr;
      ActiveVarTrace active;
--- 2016,2025 ----
  					 * indicates what's happening to
  					 * variable, plus other stuff like
  					 * TCL_GLOBAL_ONLY and
! 					 * TCL_INTERP_DESTROYED.   May also
! 					 * contain PART1_NOT_PARSEd, which
! 					 * should not be passed through
! 					 * to callbacks. */
  {
      register VarTrace *tracePtr;
      ActiveVarTrace active;
***************
*** 2069,2075 ****
  	    }
  	}
      }
! 
  
      /*
       * Invoke traces on the array containing the variable, if relevant.
--- 2072,2078 ----
  	    }
  	}
      }
!     flags &= ~PART1_NOT_PARSED;
  
      /*
       * Invoke traces on the array containing the variable, if relevant.
*** ../tcl7.4p1/./tclUnixUtil.c	Sat Jul 22 17:31:37 1995
--- ./tclUnixUtil.c	Mon Aug 28 08:57:36 1995
***************
*** 16,22 ****
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   */
  
! static char sccsid[] = "@(#) tclUnixUtil.c 1.56 95/07/22 17:31:36";
  
  #include "tclInt.h"
  #include "tclPort.h"
--- 16,22 ----
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   */
  
! static char sccsid[] = "@(#) tclUnixUtil.c 1.57 95/08/28 08:57:37";
  
  #include "tclInt.h"
  #include "tclPort.h"
***************
*** 691,696 ****
--- 691,699 ----
  	    goto error;
  	}
  	execName = Tcl_TildeSubst(interp, argv[firstArg], &buffer);
+ 	if (execName == NULL) {
+ 	    goto error;
+ 	}
  	pid = vfork();
  	if (pid == 0) {
  	    if (((inputId != -1) && (dup2(inputId, 0) == -1))
*** ../tcl7.4p1/./tclInt.h	Wed Jun 28 09:36:24 1995
--- ./tclInt.h	Fri Aug 25 15:44:50 1995
***************
*** 9,15 ****
   * See the file "license.terms" for information on usage and redistribution
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   *
!  * @(#) tclInt.h 1.105 95/06/28 09:36:23
   */
  
  #ifndef _TCLINT
--- 9,15 ----
   * See the file "license.terms" for information on usage and redistribution
   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
   *
!  * @(#) tclInt.h 1.106 95/08/25 15:44:50
   */
  
  #ifndef _TCLINT
***************
*** 415,421 ****
      Tcl_HashEntry *hPtr;	/* Pointer to the hash table entry in
  				 * interp->commandTable that refers to
  				 * this command.  Used to get a command's
! 				 * name from its Tcl_Command handle. */
      Tcl_CmdProc *proc;		/* Procedure to process command. */
      ClientData clientData;	/* Arbitrary value to pass to proc. */
      Tcl_CmdDeleteProc *deleteProc;
--- 415,425 ----
      Tcl_HashEntry *hPtr;	/* Pointer to the hash table entry in
  				 * interp->commandTable that refers to
  				 * this command.  Used to get a command's
! 				 * name from its Tcl_Command handle.  NULL
! 				 * means that the hash table entry has
! 				 * been removed already (this can happen
! 				 * if deleteProc causes the command to be
! 				 * deleted or recreated). */
      Tcl_CmdProc *proc;		/* Procedure to process command. */
      ClientData clientData;	/* Arbitrary value to pass to proc. */
      Tcl_CmdDeleteProc *deleteProc;
***************
*** 423,428 ****
--- 427,436 ----
  				 * command. */
      ClientData deleteData;	/* Arbitrary value to pass to deleteProc
  				 * (usually the same as clientData). */
+     int deleted;		/* Means that the command is in the process
+ 				 * of being deleted (its deleteProc is
+ 				 * currently executing).  Any other attempts
+ 				 * to delete the command should be ignored. */
  } Command;
  
  /*
*** ../tcl7.4p1/./changes	Fri Jul 28 10:20:44 1995
--- ./changes	Mon Sep 18 11:04:09 1995
***************
*** 1,6 ****
  Recent user-visible changes to Tcl:
  
! sccsid = %Z% %M% %I% %E% %U%
  
  1. No more [command1] [command2] construct for grouping multiple
  commands on a single command line.
--- 1,6 ----
  Recent user-visible changes to Tcl:
  
! sccsid = @(#) changes 1.16 95/09/18 11:04:07
  
  1. No more [command1] [command2] construct for grouping multiple
  commands on a single command line.
***************
*** 1242,1244 ****
--- 1242,1260 ----
  handle the case where endPtr == NULL.
  
  ----------------- Released patch 7.4p1, 7/29/95 -----------------------
+ 
+ 8/4/95 (bug fix) C-level trace callbacks for variables were sometimes
+ receiving the PART1_NOT_PARSED flag, which could cause errors in
+ subsequent Tcl library calls using the flags.
+ 
+ 8/4/95 (bug fix) Calls to toupper and tolower weren't using the
+ UCHAR macros, which caused trouble in non-U.S. locales. (JO)
+ 
+ 8/25/95 (bug fix) Undid change from 7/19, so that commands can stay
+ around while their deletion callbacks execute.  Added lots of code to
+ handle all of the reentrancy problems that this opens up.
+ 
+ 8/28/95 (bug fix) Exec wasn't handling bad user names properly, as
+ in "exec ~bogus_user/foo".
+ 
+ ----------------- Released patch 7.4p2, 9/19/95 -----------------------
